#include "utf16encode.h"
#include "exceptions/openfileexception.h"
#include "utils/fileutils.h"
#include "exceptions/filenotfoundexception.h"
#include <fstream>
#include <string>
#include <vector>

using namespace std;

Converter::Converter(){}

string Converter::utf8ToUtf16(string filePath) throw (Utf8Exception)
{
    string utf8 = readFile(filePath);
    vector<long> unicodes;
    string utf16;
    unsigned int index = 0;
    while(index < utf8.length())
    {
        char byte = utf8[index];
        char mask = byte >> 3 & ((1 << 5) - 1);
        short toRead;
        
        if((mask >> 4) == 0)
        {
            toRead = 1;
        }
        else if((mask >> 2) == 6)
        {
            toRead = 2;
        } 
        else if((mask >> 1) == 14)
        {
            toRead = 3;
        }
        else if(mask == 30)
        {
            toRead = 4;
        }
        else
        {
            throw Utf8Exception();
        }
        
        unicodes.push_back(getUniCode(utf8, index, toRead));
        index += toRead;
    }
    
    for (size_t i = 0; i < unicodes.size(); i++)
    {
        unsigned long unicode = unicodes[i];
        if (unicode <= 0xFFFF)
        {
            utf16 += unicode;
        } else
        {
            unicode -= 0x10000;
            utf16 += ((unicode >> 10) + 0xD800);
            utf16 += ((unicode & 0x3FF) + 0xDC00);
        }
    }

    return utf16;
}

long Converter::getUniCode(const string& str, const int& since, const short& toRead)
{
    long unicode = 0;
    if(toRead == 1)
    {
        unicode = str[since];
    } else
    {
        unicode = str[since] & ((1 << (7 - toRead)) - 1);
        
        for (int i = 1; i < toRead; i++)
        {
            char aux = str[since + i];
            if((aux >> 6 & 1) != 2)
            {
                throw Utf8Exception();
            }
            
            unicode <<= 6;
            unicode += aux & 0x3F;
            
        }
    }
    
    return unicode;
}

string Converter::readFile(string filePath) throw (FileException)
{
    if(!exist(filePath.c_str()))
    {
        throw FileNotFoundExpcetion(filePath.c_str());
    }
    ifstream file(filePath.c_str());
    if(!file.good())
    {
        throw OpenFileException(filePath.c_str());
    }
    
    string data;
    string buffer;
    while(getline(file, buffer))
    {
        data.append(buffer);
        data.append("\n");
    }
    data.erase(data.length() - 1);
    file.close();
    return data;
}

void Converter::writeText(std::string data, std::string output) throw(OpenFileException)
{
    ofstream file(output.c_str());
    if(!file.good())
    {
        throw OpenFileException(output.c_str());
    }
    file << data;
    file.close();
}
